#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>
#include <thread>
#include <functional>
#include <assert.h>
class CThreadPool
{
public:
    explicit CThreadPool(size_t threadCount = 8);
    ~CThreadPool();
    CThreadPool(const CThreadPool& other) = delete;
    CThreadPool& operator=(const CThreadPool &other) = delete;

    template <class T>
    void addTask(T&& task);
private:
    void runInThread();

    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex m_mtx;
    std::condition_variable m_cond;
    bool m_isClosed;
};

inline CThreadPool::CThreadPool(size_t threadCount)
{
    assert(threadCount > 0);
    m_isClosed = false;
    for (size_t i = 0; i < threadCount; i++)
    {
        workers.emplace_back(
                std::thread(std::bind(&CThreadPool::runInThread, this))
        );
    }
}
inline CThreadPool::~CThreadPool()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    m_isClosed = true;
    m_cond.notify_all();
    for (auto &thr : workers)
    {
        thr.join();
    }
}
inline void CThreadPool::runInThread()
{
    while (!m_isClosed)
    {
       std::unique_lock<std::mutex> lock(m_mtx);
       while (tasks.empty() && !m_isClosed)
       {    
            m_cond.wait(lock);
       }
       if (!tasks.empty())
       {
           auto task = std::move(tasks.front());
           tasks.pop();
           lock.unlock();
           task();
       }
    } 
}

template <class T>
inline void CThreadPool::addTask(T&& task)
{
    if(workers.size() == 0)
    {
        task();
    }
    if(!m_isClosed)
    {
        std::lock_guard<std::mutex> lock(m_mtx);
        tasks.emplace(std::forward<T>(task));
        m_cond.notify_one();
    }
}
#endif